;;; cl-synth : A software synthesiser for Common Lisp
;;; Copyright (C) 2010  Richard van Roy - (pluijzer [at] gmail [dot] com)

;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation, either version 3 of the License, or
;;; (at your option) any later version.

;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.

;;; You should have received a copy of the GNU General Public License
;;; along with this program.  If not, see <http://www.gnu.org/licenses/>

(in-package :cl-synth)

(defvar pi-f 3.1415926535897931)

(defun deg-to-rad (degrees)
  "Converts degrees to radians."
  (* degrees (/ pi 180)))

(defun between (n m x)
 (float (+  (* (/ (- m n) 10) (* x 10)) n)))

(defun interpolate (n list)
  (if (>= n (- (length list) 1)) (nth (floor n) list)
      (multiple-value-bind (fn rn) (floor n)
	(between (nth fn list)
		 (nth (+ fn 1) list) rn))))

(defun value-with-scale (n list size)
  "Gives a value from the position of a list where it a
different size."
  (interpolate (/ n (/ size (- (length list) 1))) list))

(defun scale (list size)
  "Rescales a signal to a specific size using interpolation."
  (loop for i from 0 to (- size 1)
     collect (value-with-scale i list (- size 1))))
      
;;; TODO: signal math operators must be more functional.
;;;       and it must also be possible to use a random
;;;       number of arguments.

(defun make-equal (a b &optional (interpolate t))
  "Make signal b the size of signal a."
  (if interpolate
      (let ((a2 (length a))
	    (b2 (length b)))
	(if (equal a2 b2) (list a b)
	    (list a (scale b a2))))
      (extend (smallest a b) (length (biggest a b)) 0)))
  
(defmacro intwine-waves (&body waves)
  "Combine two or more signals for multiple channel output."
  `(intwine ,@waves))

(defun operate-wave-!stretch (operator a b)
  "Preform a operation on two signals without stretching them."
  (append (mapcar operator a b)
	  (nthcdr (length (smallest a b))
		  (biggest a b))))

(defun operate-wave-stretch-single (operator a b)
  "Preform an operation on one or two signals. The second
signal may be a signal, list or number."
  (case (value-type b)
    (number
     (mapcar (lambda (x) (funcall operator x b)) a))
    (list
     (let ((w (make-equal a b)))
       (mapcar operator (first w) (second w))))))

(defun operate-wave-stretch-aux (operator list total)
  (if (not list)
      total
      (operate-wave-stretch-aux operator (rest list)
				(operate-wave-stretch-single
				 operator total (first list)))))

(defun operate-wave (operator &rest lists)
  (operate-wave-stretch-aux operator (rest lists) (first lists)))

(defun convolute-one (object position length b)
  (append (repeat 0 position)
	  (mapcar (lambda (x) (* object x)) b)
	  (repeat 0 (- length position))))
				 

(defun convolute-aux (lst b length position total)
  (if (not lst)
      total
      (convolute-aux (rest lst) b length (1+ position)
			   (mapcar #'+ total
				   (convolute-one (first lst)
						  position
						  length b)))))
(defun convolute (a b)
  (convolute-aux a b (length a) 0 
		 (repeat 0 (1- (+ (length a) (length b))))))